
//////////////////////////////////////////////////////////////////////
// PowerThread.cpp: implementation of the CPowerThread class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PowerThread.h"
#include "Msgqueue.h"


//--------------------------------------------------------------------
//Macro define
#define DEFAULT_TIMEOUT        1000ms //1000ms

//---------------------------------------------------------------------
//Initialize
CPowerThread *CPowerThread::m_pInstance = NULL;


//----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPowerThread::CPowerThread():
m_bExitThread(TRUE),
m_ulWaitTime(DEFAULT_TIMEOUT),
m_bRunning(FALSE),
m_pNotifyPower(NULL)
{
	InitLock();
}

CPowerThread::~CPowerThread()
{
	if(m_pInstance != NULL)
	{
		delete m_pInstance;
		m_pInstance = NULL;
	}

	DelLock();
}



//------------------------------------------------------------------
//Description:
//    Get the level of power from the PPOWER_BROADCAST struct
//
//Parameters:
//    pPowerInfo:[in] The struct includes the power information
//    piPercent:[out] The battery life percent.
//
//Return Values:
//    The power status
//----------------------------------------------------------------
PowerStatusType CPowerThread::GetPowerStatus(PPOWER_BROADCAST pPowerInfo, int *piPercent)
{
	PowerStatusType powStatus = POW_UNKNOW;

	if ( !pPowerInfo )
	{
		return POW_UNKNOW;
	}

	PPOWER_BROADCAST_POWER_INFO ppbpi = (PPOWER_BROADCAST_POWER_INFO) pPowerInfo->SystemPowerState;
	if ( !ppbpi )
	{
		return POW_UNKNOW;
	}

	*piPercent = ppbpi->bBatteryLifePercent; 

	if(ppbpi->bACLineStatus == AC_LINE_ONLINE)
	{        
		if(ppbpi->bBatteryFlag == BATTERY_FLAG_CHARGING)
		{
			//Charging
			powStatus = POW_CHARGING;
		}
		else
		{
			//May be full charging,or may be no battery
			powStatus = POW_CHARGEFULL;
		}
	}
	else
	{
		//Use battery

		if(0 <= ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <= 20)
		{
			powStatus = POW_VLOW;
		}
		else if(20 < ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <= 40)
		{
			powStatus = POW_LOW;
		}
		else if(40 < ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <=60)
		{
			powStatus = POW_NORMAL;
		}
		else if(60 < ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <=80)
		{
			powStatus = POW_HIGH;
		}
		else if(80 < ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <= 100)
		{
			powStatus = POW_VHIGH;
		}
		else
		{
			powStatus = POW_UNKNOW;
		}
	}

	return powStatus;
}




//------------------------------------------------------------------
//Description:
//    Thread to get the power status
//----------------------------------------------------------------
DWORD WINAPI CPowerThread::PowerThread(PVOID pArg)
{

	m_pInstance->m_bRunning = TRUE;

	BYTE pbMsgBuf[sizeof(POWER_BROADCAST) + sizeof(POWER_BROADCAST_POWER_INFO)];
	PPOWER_BROADCAST ppb = (PPOWER_BROADCAST) pbMsgBuf;
	MSGQUEUEOPTIONS msgopts;

	// Create our message queue
	memset(&msgopts, 0, sizeof(msgopts));
	msgopts.dwSize = sizeof(msgopts);
	msgopts.dwFlags = 0;
	msgopts.dwMaxMessages = 0;
	msgopts.cbMaxMessage = sizeof(pbMsgBuf);
	msgopts.bReadAccess = TRUE;

	HANDLE rghWaits[1] = { NULL };
	rghWaits[0] = CreateMsgQueue(NULL, &msgopts);
	if (!rghWaits[0])
	{
		//erro
		return 0x10;

	}

	HANDLE hReq = NULL;
	// Request notifications
	hReq = RequestPowerNotifications(rghWaits[0], PBT_POWERINFOCHANGE);
	if (!hReq)
	{
		CloseHandle( rghWaits[ 0 ] );
		//erro
		return 0x15;

	}      



	while(m_pInstance->m_bExitThread == FALSE)
	{


		DWORD dwWaitCode = MsgWaitForMultipleObjectsEx( 1, rghWaits, m_pInstance->m_ulWaitTime, QS_ALLINPUT, MWMO_INPUTAVAILABLE );
		if ( dwWaitCode == WAIT_OBJECT_0 )
		{              
			DWORD dwSize, dwFlags;
			BOOL bReadResult = ReadMsgQueue(rghWaits[0], ppb, sizeof(pbMsgBuf), &dwSize, 0, &dwFlags);
			if (bReadResult == TRUE)
			{
				int iPowPercent;
				PowerStatusType powStatus = m_pInstance->GetPowerStatus(ppb,&iPowPercent);

				m_pInstance->LockThis();
				if(m_pInstance->m_pNotifyPower != NULL)
				{
					m_pInstance->m_pNotifyPower(powStatus,iPowPercent);
				}
				m_pInstance->UnLockThis();

			}
			else
			{
				// We should never get here
				break;                
			}
		}


	}
	m_pInstance->m_bRunning = FALSE;

	return 0;
}


//------------------------------------------------------------------
//Description:
//    Get instance
//----------------------------------------------------------------
CPowerThread * CPowerThread::GetInstance()
{
	if(m_pInstance == NULL)
	{
		m_pInstance = new CPowerThread();
	}

	return m_pInstance;
}


//------------------------------------------------------------------
//Description:
//    Set the timeout for the wait thread.  It is only for the MsgWaitForMultipleObjectsEx()
//The default value is DEFAULT_TIMEOUT
//----------------------------------------------------------------
void CPowerThread::SetTimeout(ULONG ulTime)
{
	m_ulWaitTime = ulTime;
}


//------------------------------------------------------------------
//Description:
//    Get the status of thread
//
//Return Values:
//    TRUE: The thread is running for capturing the power status.
//    FALSE: No thread running.
//----------------------------------------------------------------
BOOL CPowerThread::GetRunStatus()
{
	return m_bRunning;
}


//------------------------------------------------------------------
//Description:
//    start capturing the power status.If there is thread running,
//it will return FALSE;
//
//------------------------------------------------------------------
BOOL CPowerThread::StartCapture()
{
	if(m_bRunning == TRUE)
	{
		return FALSE;
	}

	m_bExitThread = FALSE;

	//Create the thread for batter sampled    
	DWORD dwPwrThdID;
	HANDLE hdThrd = CreateThread(NULL,0,PowerThread,NULL,0,&dwPwrThdID);
	if(hdThrd == NULL)
	{
		return FALSE;
	}
	CloseHandle(hdThrd);

	return TRUE;
}



//-----------------------------------------------------------------------------
//Description:
//    Stop capturing.
//
//--------------------------------------------------------------------------------
void CPowerThread::StopCapture()
{
	m_bExitThread = TRUE;
}


//------------------------------------------------------------------
//Description:
//    Set the callback function for receive the power status
//------------------------------------------------------------------
void CPowerThread::SetCallbackFunction(void (*pCallbackFunc)(PowerStatusType powStatus, int iBatteryPercent))
{
	LockThis();
	m_pNotifyPower = pCallbackFunc;
	UnLockThis();
}



//------------------------------------------------------------------
//Description:
//    Get the callback function
//------------------------------------------------------------------
void CPowerThread::GetCallbackFunction(void (* *pCallbackFunc)(PowerStatusType powStatus, int iBatteryPercent))
{
	LockThis();
	*pCallbackFunc = m_pNotifyPower;
	UnLockThis();
}